<?php
/**
 * 	Bushido
 *
 * @category   Bushido
 * @package    Bushido_Controller
 * @subpackage Action
 * @copyright  Copyright (c) 2008-2009 Nathan Keyes
 * @author Nathan Keyes
 * @version    $Id: AssemblyLine.php 51 2009-11-15 16:21:18Z nkeyes $
 */

class Bushido_Controller_Action_AssemblyLine extends Bushido_Controller_Action
{	
	protected $_tableName;
	protected $_table;
	
	/**
	 * 
	 * @return void
	 */
	public function indexAction() 
	{
		$this->view->adminLinks = $this->_buildAdminLinks();
	}
	
	protected function _buildAdminLinks()
	{
		$html = "<h1>Modules</h1>\n";
		
		$moduleFolder = realpath('../application/modules' );
		$modules = scandir($moduleFolder);
		foreach($modules as $module)
		{
			if(substr($module, 0, 1) != '.')
			{
				$html .= "<h2>$module</h2>";
				$currentFolder = realpath('../application/modules/' . $module . '/models/DbTable' );
				if(is_dir($currentFolder))
				{
					$html .= $this->_getFolderClassLinks($currentFolder);
				}
			}
		}
		
		
		$html .= "<h1>Libraries</h1>\n";
		$libraryFolder = realpath('../library' );
		$libraries = scandir($libraryFolder);
		foreach($libraries as $library)
		{
			if(substr($library, 0, 1) != '.')
			{
				$html .= "<h2>$library</h2>";
				$currentFolder = realpath('../library/' . $library . '/DbTable' );
				if(is_dir($currentFolder))
				{
					$html .= $this->_getFolderClassLinks($currentFolder);
				}
			}
		}
		
		
		return $html;
	}
	
	protected function _getFolderClassLinks($currentFolder)
	{
		$html = '';
		$contents = scandir($currentFolder);
		
		$initialFolders = preg_split('/\//', $currentFolder);
		$initialFolders = array_slice($initialFolders, 1);
		if(array_search('modules',$initialFolders))
		{
			$folders = array_slice($initialFolders, array_search('modules',$initialFolders)+1);
			$module = $folders[array_search('modules',$folders)];
			$classNamePrefix = ucfirst($module) . '_Model_' . implode('_', array_slice($folders, array_search('modules',$folders)+2)) . '_';
		}
		else
		{
			$folders = array_slice($initialFolders, array_search('library',$initialFolders)+1);
			$library = $folders[array_search('library',$folders)];
			$classNamePrefix = ucfirst($library) . '_' . implode('_', array_slice($folders, array_search('library',$folders)+1)) . '_';
		}
		
		//Zend_Debug::dump($classNamePrefix);
		foreach($contents as $fileName)
		{
			$fileName = $currentFolder . '/' . $fileName;
			$pathInfo = pathinfo($fileName);
			
			if(is_file($fileName) /*&& $pathInfo['extension'] === 'php'*/)
			{
				$html .= $pathInfo['filename']
				. ': <a href="' . Zend_Controller_Front::getInstance()->getBaseUrl()
				. '/' . Zend_Controller_Front::getInstance()->getRequest()->getModuleName()
				. '/' . Zend_Controller_Front::getInstance()->getRequest()->getControllerName()
				. '/list/table/'
				. $classNamePrefix . $pathInfo['filename']
				. '">list</a> - <a href="'
				. Zend_Controller_Front::getInstance()->getBaseUrl()
				. '/' . Zend_Controller_Front::getInstance()->getRequest()->getModuleName()
				. '/' . Zend_Controller_Front::getInstance()->getRequest()->getControllerName()
				. '/add/table/'
				. $classNamePrefix . $pathInfo['filename']
				. '">add</a><br />' . "\n";
			}
			elseif(is_dir($fileName) && !empty($pathInfo['filename']) && (substr($pathInfo['filename'], 0, 1) != '.'))
			{
				//Zend_Debug::dump($pathInfo['filename'], 'good');
				$html .= $this->_getFolderClassLinks($fileName);
			}
			else
			{
				//Zend_Debug::dump($pathInfo['filename'], 'no good');
			}
		}
		
		return $html;
	}

	/**
	 * 
	 * @return void
	 */
	public function stepAction()
	{
		$assemblyLine = $this->_getAssemblyLine($this->_getParam('assemblyline'));	
		$assemblyLineIterator = new RecursiveIteratorIterator($assemblyLine, RecursiveIteratorIterator::SELF_FIRST);		
		$step = $this->_getStep();
		$this->view->assemblyLine = $assemblyLine;
		$currentStepNumber = 1;
		$currentStepTitle = array();
		$currentStepChain = array();
		foreach($assemblyLineIterator as $iteratorStepIndex => $currentStep)
		{
			$currentStepChain[$assemblyLineIterator->getDepth()] = $currentStep;
			$currentStepTitle[$assemblyLineIterator->getDepth()] = $currentStep->getName();
			
			if($step == $currentStepNumber)
			{
				$this->_tableName = $currentStep->getClass();
				$this->view->currentStepNumber = $currentStepNumber;
				$this->view->currentStep = $currentStep;
				$this->view->currentStepTitle = implode(' &gt; ', array_slice($currentStepTitle, 0 , $assemblyLineIterator->getDepth()+1));
				$actualCurrentStepChain = $currentStepChain;
				$currentStepIndex = $iteratorStepIndex;
				$currentStepDepth = $assemblyLineIterator->getDepth();
			}
			$currentStepNumber++;
		}
		if($step > $currentStepNumber-1)
		{
			$this->_helper->redirector('index','index');
		}
		$this->view->totalSteps = count($assemblyLine);	
		$this->_table = Bushido_Db_Table::getTable($this->_tableName);
		$form = $this->_removeReferenceFields($this->_table->getForm(), $currentStepChain, $currentStepDepth);
		
		if(count($this->view->currentStep->simultaneousSteps))
		{
			$form = $this->_addSubForms($form, $this->view->currentStep->simultaneousSteps);
		}
		
		$form->populate($this->_getAllParams());
		if($this->getRequest()->isPost() && $form->isValid($this->_getAllParams()))
		{
			$insertValues = $this->_prepareAssemblyLineInsertValues($form, $currentStepChain, $currentStepDepth);
			
			Bushido_Db_Table::getAdapter()->beginTransaction();
			try
			{
				$newId = $this->_table->insert($insertValues);
				echo __FILE__ . ' line ' . __LINE__ . "<br />\n";
				if($this->view->currentStep->getType() == Bushido_Db_Form_AssemblyLineStep::REFERENCE)// update parent
				{
					$this->_updateParent($currentStepChain, $currentStepDepth, $newId);
				}
				if(count($this->view->currentStep->simultaneousSteps))
				{
					echo __FILE__ . ' line ' . __LINE__ . "<br />\n";
					$form = $this->_addChildren($form, $this->view->currentStep->simultaneousSteps, current($this->_table->info(Zend_Db_Table::PRIMARY)), $newId);
					echo __FILE__ . ' line ' . __LINE__ . "<br />\n";
				}
				Bushido_Db_Table::getAdapter()->commit();
			}
			catch (Exception $e)
			{
				Bushido_Db_Table::getAdapter()->rollBack();
				throw $e;
			}
		
			
			$this->view->currentStep->setId($newId);
			Bushido_App::session()->assemblyLine = serialize($assemblyLine);
			
			$this->_doNextStep();
		}
		$form = $this->_completeAssemblyLineForm($form);
		$this->view->form = $form;
	}
	
	/**
	 * 
	 * @return void
	 */
	public function addAction()
	{
		
		//echo __FILE__ . ' line ' . __LINE__ . "<br />\n";
		$this->_tableName = $this->_getParam('table');
		$this->_table = Bushido_Db_Table::getTable($this->_tableName);
		//echo __FILE__ . ' line ' . __LINE__ . "<br />\n";
		$form = $this->_table->getForm();
		//echo __FILE__ . ' line ' . __LINE__ . "<br />\n";
		if(count($this->_table->getSimultaneousSteps()))
		{
			$form = $this->_addSubForms($form, $this->_table->getSimultaneousSteps());
		}
		
		$form->populate($this->_getAllParams());
		//echo __FILE__ . ' line ' . __LINE__ . "<br />\n";
		
		if(!$this->_table->assert(new Zend_Acl(), Bushido_App::getUser(), $form->getRow(), 'create'))
		{
			throw new Zend_Acl_Exception();
		}
		
		if($this->getRequest()->isPost() && $form->isValid($this->_getAllParams()))
		{
			$insertValues = $this->_prepareInsertValues($form);
			
			Bushido_Db_Table::getAdapter()->beginTransaction();
			try
			{
				$newId = $this->_table->insert($insertValues);
				
				if(count($this->_table->getSimultaneousSteps()))
				{
					$form = $this->_addChildren($form, $this->_table->getSimultaneousSteps(), current($this->_table->info(Zend_Db_Table::PRIMARY)), $newId);
				}
				Bushido_Db_Table::getAdapter()->commit();
			}
			catch (Exception $e)
			{
				Bushido_Db_Table::getAdapter()->rollBack();
				throw $e;
			}
			
			
			$this->_helper->redirector('index','index');
		}
		$form = $this->_completeForm($form);
		$this->view->table = $this->_table;
		$this->view->form = $form;
	}
	
	/**
	 * 
	 * @return void
	 */
	public function viewAction()
	{
		$this->_tableName = $this->_getParam('table');
		$this->_table = Bushido_Db_Table::getTable($this->_tableName);
		
		$pkFields = array_intersect_key($this->_getAllParams(), array_flip($this->_table->info(Zend_Db_Table::PRIMARY)));
		
		$form = $this->_table->getForm($pkFields);
		
		
		if(!$this->_table->assert(new Zend_Acl(), Bushido_App::getUser(), $form->getRow(), 'view'))
		{
			throw new Zend_Acl_Exception();
		}
		
		if(count($this->_table->getSimultaneousSteps()))
		{
			$form = $this->_addSubForms($form, $this->_table->getSimultaneousSteps());
		}
		$form->setMethod('post');
		
		$form->addElement('Button', 'Update', array('id' => 'Update', 'label' => 'Update', 'value' => 'Update', 'type' => 'submit', 'dijitParams' => array('label' => 'Update', 'value' => 'Update')));
		$form->addElement('Button', 'Delete', array('id' => 'Delete', 'label' => 'Delete', 'value' => 'Delete', 'type' => 'submit', 'dijitParams' => array('label' => 'Delete', 'value' => 'Delete')));
		
		
		if($this->getRequest()->isPost() && $form->isValid($_POST))
		{
			//echo __FILE__ . ' line: ' . __LINE__ . "<br />\n";exit;
			$whereData = array_intersect_key($form->getValues(), array_flip($this->_table->info(Zend_Db_Table::PRIMARY)));
			
			
			Bushido_Db_Table::getAdapter()->beginTransaction();
			try
			{
				if($this->_hasParam('Update'))
				{
					if(!$this->_table->assert(new Zend_Acl(), Bushido_App::getUser(), $form->getRow(), 'update'))
					{
						throw new Zend_Acl_Exception();
					}
					$form->update($this->_table, $form->getValues(), $whereData);
					
					if(count($this->_table->getSimultaneousSteps()))
					{
						$this->_updateChildren($form, $this->_table->getSimultaneousSteps());
					}
				}
				elseif($this->_hasParam('Delete'))
				{
					if(!$this->_table->assert(new Zend_Acl(), Bushido_App::getUser(), $form->getRow(), 'delete'))
					{
						throw new Zend_Acl_Exception();
					}
					$form->delete($this->_table, $whereData);
				}
				Bushido_Db_Table::getAdapter()->commit();
			}
			catch (Exception $e)
			{
				Bushido_Db_Table::getAdapter()->rollBack();
				throw $e;
			}
			
			
			$this->_helper->redirector('index','index');
			
		}
		elseif(!$this->getRequest()->isPost())
		{

			$row = $this->_table->search($this->_getAllParams())->current();
			//Zend_Debug::dump($row);exit;
			if(!$row)
			{
				throw new Bushido_Db_Table_Exception_RowNotFound;
			}
			$form->populate($row->toArray());
		}
		
		$this->view->table = $this->_table;
		$this->view->form = $form;
	}
	
	
	/**
	 * 
	 * @return void
	 */
	public function listAction()
	{
		$this->_tableName = $this->_getParam('table');
		$table = Bushido_Db_Table::getTable($this->_tableName);
	
		$this->view->tableName = $this->_tableName;
		$this->view->table = $table;
	}
	
	/**
	 * 
	 * @return void
	 */
	public function jsonAction()
	{
		$this->_helper->layout->disableLayout();
		$this->_tableName = $this->_getParam('table');
		
		$sortColumn = $this->_getParam('sort');
		$sortDirection = $this->_getParam('dir', 'asc');
		$limit = $this->_getParam('results', 30);
		$offset = $this->_getParam('startIndex', 0);
		$table = Bushido_Db_Table::getTable($this->_tableName);
		
		$rowset = $table->search($this->_getAllParams(), $sortColumn, $sortDirection, $limit, $offset, $totalRecords);
		
		//Zend_Debug::dump($rowset);
		//	$rowset = $table->fetchAll();

		$this->view->data = Bushido_Db_Table::rowsetGridJson($rowset, $sortColumn, $sortDirection, $limit, $offset, $totalRecords);
	}
	
	public function auditAction()
	{
		$this->_tableName = 'Bushido_DbTable_AuditLog';
		$table = Bushido_Db_Table::getTable($this->_tableName);
	
		$this->view->tableName = $this->_tableName;
		$this->view->table = $table;
	}
	
	/**
	 * 
	 * @param $form
	 * @return Bushido_Db_Form
	 */
	protected function _addSubForms(Bushido_Db_Form $form, $steps)
	{
		foreach($steps as $step)
		{
			$form = $this->_addSubForm($form, $step);
		}
		return $form;
	}
	
	protected function _addSubForm(Bushido_Db_Form $form, Bushido_Db_Form_AssemblyLineStep $step)
	{
		switch($step->type)
		{
			case Bushido_Db_Form_AssemblyLineStep::MANY_TO_MANY:
				$form->addManyToManySubForm($step->name, $step->pairedClass, $step->class);
				break;
			case Bushido_Db_Form_AssemblyLineStep::CHILD:
				$form->addOneToOneSubForm($step->name, $step->class);
				break;
			case Bushido_Db_Form_AssemblyLineStep::REFERENCE:
				$form->addOneToOneSubForm($step->name, $step->class);
				break;
		}
		
		return $form;
	}
	
	
	/**
	 * 
	 * @param $form
	 * @return Bushido_Db_Form
	 */
	protected function _addChildren(Bushido_Db_Form $form, $steps, $parentField, $parentId)
	{
		foreach($steps as $simultaneousStep)
		{
			switch($simultaneousStep->type)
			{
				case Bushido_Db_Form_AssemblyLineStep::MANY_TO_MANY:
					$this->_addManyToManyChild($form, $simultaneousStep, $parentField, $parentId);
					break;
				case Bushido_Db_Form_AssemblyLineStep::CHILD:
					echo __FILE__ . ' line ' . __LINE__ . "<br />\n";
					$this->_addChild($form, $simultaneousStep, $parentField, $parentId);
					break;
			}
		}
		return $form;
	}
	
	protected function _addManyToManyChild(Bushido_Db_Form $form, Bushido_Db_Form_AssemblyLineStep $step, $parentField, $parentId)
	{
		$middleManTable = Bushido_Db_Table::getTable($step->class);
				
		$defaultValues = array_merge($middleManTable->getDefaultValues(), array($parentField => $parentId));
		$form->addManyToManyChildren
		(
			array
			(
				$step->name =>
				array
				(
					'childClassName' => $step->pairedClass,
					'childClassMiddleMan' => $step->class
				)
			),
			$defaultValues
		);
	}
	
	protected function _addChild(Bushido_Db_Form $form, Bushido_Db_Form_AssemblyLineStep $step, $parentField, $parentId)
	{
		echo __FILE__ . ' line ' . __LINE__ . "<br />\n";
		$classTable = Bushido_Db_Table::getTable($step->class);
		
		
		$defaultValues = array_merge($classTable->getDefaultValues(), array($parentField => $parentId));
		$form->addOneToOneChildren
		(
			array
			(
				$step->name =>
				array
				(
					'childClassName' => $step->class,
				)
			),
			$defaultValues
		);
	}
	
	
/**
	 * 
	 * @param $form
	 * @return Bushido_Db_Form
	 */
	protected function _updateChildren(Bushido_Db_Form $form, $steps)
	{
		foreach($steps as $simultaneousStep)
		{
			switch($simultaneousStep->type)
			{
				case Bushido_Db_Form_AssemblyLineStep::MANY_TO_MANY:
					$this->_updateManyToManyChild($form, $simultaneousStep);
					break;
				case Bushido_Db_Form_AssemblyLineStep::CHILD:
					$this->_updateChild($form, $simultaneousStep);
					break;
			}
		}
		return $form;
	}
	
	protected function _updateManyToManyChild(Bushido_Db_Form $form, Bushido_Db_Form_AssemblyLineStep $step)
	{
		
		
		if(current($form->getValue($step->name . '_subForm')))
		{
			//var_dump(current($form->getValue('Assignees_subForm')));exit;
			$form->deleteManyToManyChildren
			(
				array
				(
					$step->name =>
					array
					(
						'childClassName' => $step->pairedClass,
						'childClassMiddleMan' => $step->class
					)
				),
				array
				(
					new Zend_Db_Expr( current(Bushido_Db_Table::getTable($step->class)->info(Bushido_Db_Table_Abstract::PRIMARY)) . ' NOT IN (' . join(',', current($form->getValue($step->name . '_subForm'))) . ')')
				)
			);
		}
		else
		{
			$form->deleteManyToManyChildren
			(
				array
				(
					$step->name =>
					array
					(
						'childClassName' => $step->pairedClass,
						'childClassMiddleMan' => $step->class
					)
				)
			);
		}
		$form->filterExistingManyToManyChildren(array($step->name =>
					array
					(
						'childClassName' => $step->pairedClass,
						'childClassMiddleMan' => $step->class
					)));
					
					
		$this->_addManyToManyChild($form, $step, $parentField, $parentId);
	}
	
	protected function _updateChild(Bushido_Db_Form $form, Bushido_Db_Form_AssemblyLineStep $step)
	{
		$form->updateOneToOneChildren(array($step->name =>array('childClassName' => $step->class)));
	}
	
	
	/**
	 * 
	 * @param $assemblyLineClassName
	 * @return Bushido_Db_Form_AssemblyLine
	 */
	protected function _getAssemblyLine($assemblyLineClassName)
	{
		if(isset(Bushido_App::session()->assemblyLine) && $this->_getStep() > 1)
		{
			return unserialize(Bushido_App::session()->assemblyLine);
		}
		else
		{
			return new $assemblyLineClassName();
		}
	}
	
	/**
	 * 
	 * @param $form
	 * @return Bushido_Db_Form
	 */
	protected function _removeReferenceFields(Bushido_Db_Form $form, $currentStepChain, $currentDepth)
	{
		
		foreach($this->_table->getReferenceMap() as $rule => $referenceData)
		{
			if($currentDepth > 0)
			{
				foreach($currentStepChain[$currentDepth-1]->getDependantSteps() as $dependantStep)
				{
					if($rule == $dependantStep->getRule())
					{
	
						$form->removeElement($referenceData['columns']);
					}
				}
			}
			if(count($currentStepChain[$currentDepth]))
			{
				foreach($currentStepChain[$currentDepth]->getDependantSteps() as $dependantStep)
				{
					if($rule == $dependantStep->getRule())
					{
	
						$form->removeElement($referenceData['columns']);
					}
				}
			}
		}
		return $form;
	}
	
	/**
	 * 
	 * @return int
	 */
	protected function _getStep()
	{
		$step = (int) $this->_getParam('step', 1);
		$step = $step < 1 ? 1: $step;
		return $step;
	}
	
	
	/**
	 * 
	 * @param $form
	 * @return array
	 */
	protected function _prepareAssemblyLineInsertValues(Bushido_Db_Form $form, $currentStepChain, $currentStepDepth)
	{
		$insertValues = $this->_prepareInsertValues($form);
		
		if($this->view->currentStep->getType() == Bushido_Db_Form_AssemblyLineStep::CHILD && $currentStepDepth > 0)// update self 
		{
			$referenceMap = $this->_table->getReferenceMap();
			$insertValues[$referenceMap[$this->view->currentStep->getRule()]['columns']] = $currentStepChain[$currentStepDepth-1]->id;
		}
	
		return $insertValues;
	}
	
	/**
	 * 
	 * @param $form
	 * @return array
	 */
	protected function _prepareInsertValues(Bushido_Db_Form $form)
	{
		$insertValues = array_merge($form->getValues(), $this->_table->getdefaultValues());
		
		$insertValues = array_intersect_key($insertValues, $this->_table->fetchNew()->toArray());

		foreach($insertValues as $field => $value)
		{
			$element = $form->getElement($field);
			if($element instanceof Zend_Form_Element_File)
			{
				
				if(!$element->getTransferAdapter()->isUploaded() || !$element->receive())
				{
					throw new Exception('Error receiving file.');
				}
				;
				$fileInfo = $element->getTransferAdapter()->getFileInfo();
				$fileInfo = $fileInfo[$field];
				$insertValues['filename'] = $fileInfo['name'];
				$insertValues['size'] = $fileInfo['size'];
				
				$insertValues['mime_type'] = $fileInfo['type'];
				$fileName = $element->getFileName();
				$fileHandle = fopen($fileName, 'r');
				
				$insertValues['content'] = fread($fileHandle, $insertValues['size']);
			}
		}
		return $insertValues;
	}
	
	/**
	 * 
	 * @param $currentStepChain
	 * @param $currentStepDepth
	 * @param $newId
	 * @return void
	 */
	protected function _updateParent($currentStepChain, $currentStepDepth, $newId)
	{
		$parent = Bushido_Db_Table::getTable($currentStepChain[$currentStepDepth-1]->getClass())->find($currentStepChain[$currentStepDepth-1]->id)->current();
		$referenceMap = $parent->getTable()->getReferenceMap();
		$parent->{$referenceMap[$this->view->currentStep->getRule()]['columns']} = $newId;
		$parent->save();
	}
	
	
	/**
	 * 
	 * @param $step
	 * @param $assemblyLineClassName
	 * @return void
	 */
	protected function _doNextStep()
	{
		$assemblyLine = $this->_getParam('assemblyline');
		$step = $this->_getStep();
		
		if($step < $this->view->totalSteps)
		{
			$this->_helper->redirector($this->getRequest()->getActionName(), $this->getRequest()->getControllerName(), null, array('assemblyline' => $assemblyLine, 'step' => $step+1,));
		}
		else
		{
			unset(Bushido_App::session()->assemblyLine);
			$this->_helper->redirector('index','index');
		}
	}
	
	
	/**
	 * 
	 * @param $form
	 * @return Bushido_Db_Form
	 */
	protected function _completeAssemblyLineForm(Bushido_Db_Form $form)
	{
		$assemblyLine = $this->_getParam('assemblyline');
		$step = $this->_getStep();
		
		$formActionParams = array('assemblyline' => $assemblyLine, 'step' => $step,);
		$formAction = $this->_helper->url($this->getRequest()->getActionName(), null, null, $formActionParams);
		$form->setAction($formAction);
		$form->setAttrib('id', $form->filterName($this->_table->getName() . 'Form'));
		$form->setMethod('post');
		
		$form->addElement('Button', 'Submit', array('id' => 'Submit', 'label' => 'Add', 'value' => 'Add', 'type' => 'submit', 'dijitParams' => array('label' => 'Add', 'value' => 'Add')));
		
		return $form;
	}
	
	/**
	 * 
	 * @param $form
	 * @return Bushido_Db_Form
	 */
	protected function _completeForm(Bushido_Db_Form $form)
	{
		$table = $this->_getParam('table');
		
		$formActionParams = array('table' => $table,);
		$formAction = $this->_helper->url($this->getRequest()->getActionName(), null, null, $formActionParams);
		$form->setAction($formAction);
		$form->setAttrib('id', $form->filterName($this->_table->getName() . 'Form'));
		$form->setMethod('post');
		
		$form->addElement('Button', 'Submit', array('id' => 'Submit', 'label' => 'Add', 'value' => 'Add', 'type' => 'submit', 'dijitParams' => array('label' => 'Add', 'value' => 'Add')));
		
		return $form;
	}
}